﻿using Microsoft.Extensions.Configuration;
using PHMEE.CommonUtils;
using PHMEE.Constants;
using PHMEE.DTOS.StatusMonitor;
using PHMEE.Entities.BaseInfo.Units;
using PHMEE.Entities.DaqInfo.TdDevices;
using PHMEE.IServices.StatusMonitor;
using PHMEE.TDengineInfrastructure;
using PHMEE.ToolKits;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using PHMEE.EnergyEfficiency;
using TDengine.Data.Client;
using Volo.Abp.Domain.Repositories;
using static PHMEE.Constants.PhmeeEnum;

namespace PHMEE.Services.StatusMonitor
{
    /// <summary>
    /// 船舶状态监测
    /// </summary>
    public class VesselStatusMonitorAppService : PHMEEAppService, IVesselStatusMonitorAppService
    {
        private readonly TdEngineCommonAppService tdengineAppService;
        private readonly IVesselRepository vesselRepository;
        private readonly IPointRepository pointRepository;
        private readonly IRealTimeAppService realTimeAppService;
        private readonly IPHMEETimeProvider _phmeeTimeProvider;
        private readonly IGpsTdRepository _gpsTdRepository;
        private readonly IHttpClientFactory _httpClientFactory;
        private readonly RedisHelper _redisHelper;

        public VesselStatusMonitorAppService(TdEngineCommonAppService tdengineAppService, IVesselRepository vesselRepository, IPointRepository pointRepository, IRealTimeAppService realTimeAppService,
        IPHMEETimeProvider phmeeTimeProvider, IGpsTdRepository gpsTdRepository, IHttpClientFactory httpClientFactory, RedisHelper redisHelper)
        {
            this.tdengineAppService = tdengineAppService;
            this.vesselRepository = vesselRepository;
            this.pointRepository = pointRepository;
            this.realTimeAppService = realTimeAppService;
            _phmeeTimeProvider = phmeeTimeProvider;
            _gpsTdRepository = gpsTdRepository;
            _httpClientFactory = httpClientFactory;
            _redisHelper = redisHelper;
        }


        /// <summary>
        /// 获取船舶参数
        /// </summary>
        /// <param name="VesselId"></param>
        /// <returns></returns>
        public async Task<VesselMainModel> GetVesselMainParams(Guid VesselId)
        {
            var model = new VesselMainModel();

            var vessel = await vesselRepository.PhmeeGetById(VesselId);

            if (vessel != null)
            {
                model.VesselId = vessel.Id;

                model.VesselName = vessel.DisplayName;

                model.IMO = vessel.IMO;

                model.CallSign = vessel.CallSign;

                model.Type = vessel.Type;

                model.TypeString = vessel.Type.ToString();

                model.DeadWeightTon = vessel.DeadWeightTon;

                model.NetTon = vessel.NetTon;

                model.GrossTon = vessel.GrossTon;

                model.FlagState = vessel.FlagState;

                model.ClassificationSociety = vessel.ClassificationSociety;

                model.EEXI = vessel.EEXI;

                model.EEDI = vessel.EEDI;

            }

            return model;
        }

        /// <summary>
        /// 获取航行参数
        /// </summary>
        /// <param name="VesselId"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<VesselNavigationModel> GetVesselNavigationParams(Guid VesselId, VesselBaseInput input)
        {
            var model = new VesselNavigationModel();

            await Task.Run(() =>
            {
                //model.EstimatedArrival = DateTime.UtcNow.PhmeeToFriendlyDateTimeSpecialString();

                //model.NextPort = "Taranto";

                //model.SpeedOverWater = 11.5;

                //model.DeadWeight = 8000;

            });

            return model;
        }

        /// <summary>
        /// 获取GPS参数
        /// </summary>
        /// <param name="VesselId"></param>
        /// <param name="OnVessel"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<VesselGpsModel> GetVesselGpsParams(Guid VesselId, bool OnVessel, VesselBaseInput input)
        {
            VesselGpsModel model = new VesselGpsModel();

            // 船端 查询缓存中实时数据
            if (OnVessel)
            {
                var modelCache = await realTimeAppService.GetGpsCacheDataAsync();

                if (modelCache != null)
                {

                    model.LongitudeOrg = modelCache.Longitude;

                    model.LatitudeOrg = modelCache.Latitude;

                    model.SpeedOverGroundOrg = modelCache.GroundSpeed;
                }

            }
            else
            {
                var vessel = await vesselRepository.PhmeeGetById(VesselId);

                if (vessel != null)
                {
                    var pointQuery = await pointRepository.PhmeeGetQuery();

                    pointQuery = pointQuery.Where(new PointSpecificationVessel(vessel.Id));

                    List<Point> points = pointQuery.ToList();

                    var point经度 = points.FirstOrDefault(item => item.Type == PointTypeEnum.Longitude && item.Tag == BaseInfoTagEnum.Longitude);

                    var point纬度 = points.FirstOrDefault(item => item.Type == PointTypeEnum.Latitude && item.Tag == BaseInfoTagEnum.Latitude);

                    var point对地航速 = points.FirstOrDefault(item => item.Type == PointTypeEnum.Speed && item.Tag == BaseInfoTagEnum.SpeedOverGround);

                    await tdengineAppService.FetchTdEngineData(point经度, input.DateTime, model, (reader, attributes, model) =>
                    {
                        var attrTs = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdGps.ts);

                        var attrLongitude = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdGps.Longitude);

                        model.LongitudeTs = tdengineAppService.GetTdEngineTypeValue<DateTime>(reader, attrTs);

                        model.LongitudeOrg = tdengineAppService.GetTdEngineTypeValue<float?>(reader, attrLongitude);
                    });

                    await tdengineAppService.FetchTdEngineData(point纬度, input.DateTime, model, (reader, attributes, model) =>
                    {
                        var attrTs = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdGps.ts);

                        var attrLatitude = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdGps.Latitude);

                        model.LatitudeTs = tdengineAppService.GetTdEngineTypeValue<DateTime>(reader, attrTs);

                        model.LatitudeOrg = tdengineAppService.GetTdEngineTypeValue<float?>(reader, attrLatitude);

                    });

                    await tdengineAppService.FetchTdEngineData(point对地航速, input.DateTime, model, (reader, attributes, model) =>
                    {
                        var attrTs = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdGps.ts);

                        var attrSpeedOverGround = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdGps.GroundSpeed);

                        model.SpeedOverGroundTs = tdengineAppService.GetTdEngineTypeValue<DateTime>(reader, attrTs);

                        model.SpeedOverGroundOrg = tdengineAppService.GetTdEngineTypeValue<float?>(reader, attrSpeedOverGround);

                    });
                }

            }

            return model;
        }

        /// <summary>
        /// 获取Draft参数
        /// </summary>
        /// <param name="VesselId"></param>
        /// <param name="OnVessel"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<VesselDraftModel> GetVesselDraftParams(Guid VesselId, bool OnVessel, VesselBaseInput input)
        {
            VesselDraftModel model = new VesselDraftModel();

            var vessel = await vesselRepository.PhmeeGetById(VesselId);

            if (vessel != null)
            {
                var pointQuery = await pointRepository.PhmeeGetQuery();

                pointQuery = pointQuery.Where(new PointSpecificationVessel(vessel.Id));

                List<Point> points = pointQuery.ToList();

                var point艏部吃水 = points.FirstOrDefault(item => item.Type == PointTypeEnum.Draft && item.Tag == BaseInfoTagEnum.BowDraft);

                var point艉部吃水 = points.FirstOrDefault(item => item.Type == PointTypeEnum.Draft && item.Tag == BaseInfoTagEnum.SternDraft);

                var point左舷吃水 = points.FirstOrDefault(item => item.Type == PointTypeEnum.Draft && item.Tag == BaseInfoTagEnum.PortsideDraft);

                var point右舷吃水 = points.FirstOrDefault(item => item.Type == PointTypeEnum.Draft && item.Tag == BaseInfoTagEnum.StarboardDraft);

                var point富余水深 = points.FirstOrDefault(item => item.Type == PointTypeEnum.Draft && item.Tag == BaseInfoTagEnum.UnderKeel);

                await tdengineAppService.FetchTdEngineData(point艏部吃水, input.DateTime, model, (reader, attributes, model) =>
                {
                    var attrTs = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.ts);

                    var attrBowDraft = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.Value);

                    model.BowDraftTs = tdengineAppService.GetTdEngineTypeValue<DateTime>(reader, attrTs);

                    model.BowDraftOrg = tdengineAppService.GetTdEngineTypeValue<float?>(reader, attrBowDraft);

                });

                await tdengineAppService.FetchTdEngineData(point艉部吃水, input.DateTime, model, (reader, attributes, model) =>
                {
                    var attrTs = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.ts);

                    var attrSternDraft = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.Value);

                    model.SternDraftTs = tdengineAppService.GetTdEngineTypeValue<DateTime>(reader, attrTs);

                    model.SternDraftOrg = tdengineAppService.GetTdEngineTypeValue<float?>(reader, attrSternDraft);

                });

                await tdengineAppService.FetchTdEngineData(point左舷吃水, input.DateTime, model, (reader, attributes, model) =>
                {
                    var attrTs = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.ts);

                    var attrPortsideDraft = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.Value);

                    model.PortsideDraftTs = tdengineAppService.GetTdEngineTypeValue<DateTime>(reader, attrTs);

                    model.PortsideDraftOrg = tdengineAppService.GetTdEngineTypeValue<float?>(reader, attrPortsideDraft);

                });

                await tdengineAppService.FetchTdEngineData(point右舷吃水, input.DateTime, model, (reader, attributes, model) =>
                {
                    var attrTs = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.ts);

                    var attrStarboardDraft = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.Value);

                    model.StarboardDraftTs = tdengineAppService.GetTdEngineTypeValue<DateTime>(reader, attrTs);

                    model.StarboardDraftOrg = tdengineAppService.GetTdEngineTypeValue<float?>(reader, attrStarboardDraft);

                });

                await tdengineAppService.FetchTdEngineData(point富余水深, input.DateTime, model, (reader, attributes, model) =>
                {
                    var attrTs = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.ts);

                    var attrUnderKeel = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.Value);

                    model.UnderKeelTs = tdengineAppService.GetTdEngineTypeValue<DateTime>(reader, attrTs);

                    model.UnderKeelOrg = tdengineAppService.GetTdEngineTypeValue<float?>(reader, attrUnderKeel);

                });

            }

            return model;

        }

        /// <summary>
        /// 获取MainEngine参数
        /// </summary>
        /// <param name="VesselId"></param>
        /// <param name="OnVessel"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<VesselMainEngineModel> GetVesselMainEngineParams(Guid VesselId, bool OnVessel, VesselBaseInput input)
        {
            VesselMainEngineModel model = new VesselMainEngineModel();

            var vessel = await vesselRepository.PhmeeGetById(VesselId);

            if (vessel != null)
            {
                var unit = vessel.Units.OrderBy(item => item.Number).FirstOrDefault(item => item.Type == UnitEnum.MainEngineUnit) as MainEngineUnit;

                if (unit != null)
                {
                    var equipment主机 = unit.Equipments.OrderBy(item => item.Number).FirstOrDefault(item => item.Type == EquipmentEnum.MainEngine) as MainEngine;

                    if (equipment主机 != null)
                    {

                        var pointQuery = await pointRepository.PhmeeGetQuery();

                        pointQuery = pointQuery.Where(new PointSpecificationEquipment(equipment主机.Id));

                        List<Point> points = pointQuery.ToList();

                        var point主机功率 = points.FirstOrDefault(item => item.Type == PointTypeEnum.Power && item.Tag == BaseInfoTagEnum.Power);

                        var point主机负荷 = points.FirstOrDefault(item => item.Type == PointTypeEnum.Load && item.Tag == BaseInfoTagEnum.Load);

                        var point主机转速 = points.FirstOrDefault(item => item.Type == PointTypeEnum.RevSpeed && item.Tag == BaseInfoTagEnum.Speed);

                        var point主机即时油耗 = points.FirstOrDefault(item => item.Type == PointTypeEnum.FuelConsumption && item.Tag == BaseInfoTagEnum.Instant);

                        await tdengineAppService.FetchTdEngineData(point主机功率, input.DateTime, model, (reader, attributes, model) =>
                        {
                            var attrTs = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.ts);

                            var attrPower = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.Value);

                            model.MainEnginePowerKwTs = tdengineAppService.GetTdEngineTypeValue<DateTime>(reader, attrTs);

                            model.MainEnginePowerKwOrg = tdengineAppService.GetTdEngineTypeValue<float?>(reader, attrPower);

                        });

                        await tdengineAppService.FetchTdEngineData(point主机负荷, input.DateTime, model, (reader, attributes, model) =>
                        {
                            var attrTs = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.ts);

                            var attrLoad = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.Value);

                            model.MainEngineLoadTs = tdengineAppService.GetTdEngineTypeValue<DateTime>(reader, attrTs);

                            model.MainEngineLoadOrg = tdengineAppService.GetTdEngineTypeValue<float?>(reader, attrLoad);

                        });

                        await tdengineAppService.FetchTdEngineData(point主机转速, input.DateTime, model, (reader, attributes, model) =>
                        {
                            var attrTs = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.ts);

                            var attrSpeed = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.Value);

                            model.MainEngineSpeedRpmTs = tdengineAppService.GetTdEngineTypeValue<DateTime>(reader, attrTs);

                            model.MainEngineSpeedRpmOrg = tdengineAppService.GetTdEngineTypeValue<float?>(reader, attrSpeed);

                        });

                        await tdengineAppService.FetchTdEngineData(point主机即时油耗, input.DateTime, model, (reader, attributes, model) =>
                        {
                            var attrTs = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.ts);

                            var attrInstant = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdAmsAnalog.Value);

                            model.MainEngineInstantTs = tdengineAppService.GetTdEngineTypeValue<DateTime>(reader, attrTs);

                            model.MainEngineInstantOrg = tdengineAppService.GetTdEngineTypeValue<float?>(reader, attrInstant);

                        });

                    }
                }
            }

            return model;

        }

        /// <summary>
        /// 获取ShaftSystem参数
        /// </summary>
        /// <param name="VesselId"></param>
        /// <param name="OnVessel"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<VesselShaftSystemModel> GetVesselShaftSystemParams(Guid VesselId, bool OnVessel, VesselBaseInput input)
        {
            VesselShaftSystemModel model = new VesselShaftSystemModel();

            var vessel = await vesselRepository.PhmeeGetById(VesselId);

            if (vessel != null)
            {
                var unit = vessel.Units.OrderBy(item => item.Number).FirstOrDefault(item => item.Type == UnitEnum.MainEngineUnit) as MainEngineUnit;

                if (unit != null)
                {
                    var equipment轴系 = unit.Equipments.OrderBy(item => item.Number).FirstOrDefault(item => item.Type == EquipmentEnum.ShaftSystem) as ShaftSystem;

                    if (equipment轴系 != null)
                    {
                        equipment轴系.DeserializeProperties();

                        model.MaxShaftPower = equipment轴系.RatedPower;

                        model.MaxShaftSpeed = equipment轴系.RatedSpeed;

                        model.MaxShaftTorque = equipment轴系.Torque;

                        var component中间轴 = equipment轴系.Components.OrderBy(item => item.Number).FirstOrDefault(item => item.Type == ComponentEnum.MiddleShaftComponent);

                        if (component中间轴 != null)
                        {
                            var pointQuery = await pointRepository.PhmeeGetQuery();

                            pointQuery = pointQuery.Where(new PointSpecificationComponent(component中间轴.Id));

                            List<Point> points = pointQuery.ToList();

                            var point轴功率 = points.FirstOrDefault(item => item.Type == PointTypeEnum.Power && item.Tag == BaseInfoTagEnum.Power);

                            var point轴转速 = points.FirstOrDefault(item => item.Type == PointTypeEnum.RevSpeed && item.Tag == BaseInfoTagEnum.Speed);

                            var point轴扭矩 = points.FirstOrDefault(item => item.Type == PointTypeEnum.Torque && item.Tag == BaseInfoTagEnum.Torque);

                            await tdengineAppService.FetchTdEngineData(point轴功率, input.DateTime, model, (reader, attributes, model) =>
                            {
                                var attrTs = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdShaftPowerMeter.ts);

                                var attrPower = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdShaftPowerMeter.Power);

                                model.ShaftPowerTs = tdengineAppService.GetTdEngineTypeValue<DateTime>(reader, attrTs);

                                model.ShaftPowerOrg = tdengineAppService.GetTdEngineTypeValue<float?>(reader, attrPower);

                            });

                            await tdengineAppService.FetchTdEngineData(point轴转速, input.DateTime, model, (reader, attributes, model) =>
                            {
                                var attrTs = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdShaftPowerMeter.ts);

                                var attrSpeed = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdShaftPowerMeter.Speed);

                                model.ShaftSpeedTs = tdengineAppService.GetTdEngineTypeValue<DateTime>(reader, attrTs);

                                model.ShaftSpeedOrg = tdengineAppService.GetTdEngineTypeValue<float?>(reader, attrSpeed);

                            });

                            await tdengineAppService.FetchTdEngineData(point轴扭矩, input.DateTime, model, (reader, attributes, model) =>
                            {
                                var attrTs = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdShaftPowerMeter.ts);

                                var attrTorque = attributes.FirstOrDefault(item => item.Name == PhmeeConst.TdShaftPowerMeter.Torque);

                                model.ShaftTorqueTs = tdengineAppService.GetTdEngineTypeValue<DateTime>(reader, attrTs);

                                model.ShaftTorqueOrg = tdengineAppService.GetTdEngineTypeValue<float?>(reader, attrTorque);

                            });

                        }
                    }
                }
            }

            return model;
        }


        public async Task<CurrentWeatherDto> GetCurrentWeatherAsync(Guid vesselId)
        {
            var redisKey = $"{vesselId}_CurrentWeatherData";

            var cachedData = await _redisHelper.GetStringAsync(redisKey);
            if (!string.IsNullOrEmpty(cachedData))
            {
                return JsonSerializer.Deserialize<CurrentWeatherDto>(cachedData);
            }

            var utcNow = _phmeeTimeProvider.GetUtcNow();
            var gpsTde = await _gpsTdRepository.LatestOrDefaultAsync(vesselId, utcNow);

            if (gpsTde == null)
            {
                return new();
            }

            float lat = gpsTde.Latitude;
            float lon = gpsTde.Longitude;

            var url = $"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}&current=temperature_2m,relative_humidity_2m,weather_code,pressure_msl,wind_speed_10m,wind_direction_10m,wind_gusts_10m";

            var httpClient = _httpClientFactory.CreateClient();
            httpClient.DefaultRequestHeaders.Accept.Add(new("application/json"));
            var response = await httpClient.GetAsync(url);

            var content = await response.Content.ReadAsStringAsync();
            var weatherData = JsonSerializer.Deserialize<OpenMeteoCurrentWeatherResponse>(content);

            var currentWeatherDto = new CurrentWeatherDto
            {
                Temperature2m = weatherData.Current.Temperature2m,
                RelativeHumidity2m = weatherData.Current.RelativeHumidity2m,
                WeatherCode = weatherData.Current.WeatherCode,
                PressureMsl = weatherData.Current.PressureMsl,
                WindSpeed10m = weatherData.Current.WindSpeed10m,
                WindDirection10m = weatherData.Current.WindDirection10m,
                WindGusts10m = weatherData.Current.WindGusts10m
            };

            await _redisHelper.SetStringAsync(
                redisKey,
                JsonSerializer.Serialize(currentWeatherDto),
                TimeSpan.FromHours(12) // 每次查询，缓存 12 小时
            );

            return currentWeatherDto;
        }
    }
}
